package com.example.demo2.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo2.mapper.HotelMapper;
import com.example.demo2.model.dtos.QueryDto;
import com.example.demo2.model.pojo.Hotel;
import com.example.demo2.model.pojo.HotelElastic;
import com.example.demo2.model.vos.GroupVo;
import com.example.demo2.model.vos.HotelPageVo;
import com.example.demo2.service.HotelService;
import com.example.demo2.utils.ResponseParseUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class HotelServiceImpl extends ServiceImpl<HotelMapper, Hotel> implements HotelService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public HotelPageVo search(QueryDto queryDto) throws IOException {
        HotelPageVo pageVo = new HotelPageVo();

        SearchRequest searchReq = new SearchRequest("hotel");
        SearchSourceBuilder sourceBuilder = searchReq.source();

        //设置条件
        //获取布尔查询条件构建器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //获取must条件容器（算分排序查询）
        List<QueryBuilder> must = boolQueryBuilder.must();
        //添加关键字条件
        String keyword = queryDto.getKeyword();
        if (!StringUtils.isEmpty(keyword)){
            must.add(QueryBuilders.matchQuery("all",keyword));
        }
        //添加城市条件
        List<String> cityNames = queryDto.getCityNames();
        if (cityNames!=null && cityNames.size()!=0){
           /* BoolQueryBuilder cityBoolBuilder = QueryBuilders.boolQuery();
            List<QueryBuilder> should = cityBoolBuilder.should();
            cityNames.forEach(cityName->{
                should.add(QueryBuilders.termQuery("city",cityName));
            });
            must.add(cityBoolBuilder);*/
            must.add(QueryBuilders.termsQuery("city",cityNames.toArray(new String[0])));
        }
        //添加星级条件
        List<String> starNames = queryDto.getStarNames();
        if (starNames!=null && starNames.size()!=0){
            must.add(QueryBuilders.termsQuery("starName",starNames.toArray(new String[0])));
        }
        //添加品牌条件
        List<String> brandNames = queryDto.getBrandNames();
        if (brandNames!=null && brandNames.size()!=0){
            must.add(QueryBuilders.termsQuery("brand",brandNames.toArray(new String[0])));
        }


        sourceBuilder.query(boolQueryBuilder);

        //设置分页参数
        int begin = (queryDto.getPageNum()-1)* queryDto.getSize();
        sourceBuilder.from(begin);
        sourceBuilder.size(queryDto.getSize());

        //按照城市分组查询
        sourceBuilder.aggregation(AggregationBuilders.terms("cityTerms").field("city").size(10));
        //按照星级分组查询
        sourceBuilder.aggregation(AggregationBuilders.terms("starTerms").field("starName").size(10));
        //按照品牌分组查询
        sourceBuilder.aggregation(AggregationBuilders.terms("brandTerms").field("brand").size(10));

        SearchResponse searchResponse = client.search(searchReq, RequestOptions.DEFAULT);

        //解析文档集合
        List<HotelElastic> hotelElastics = ResponseParseUtils.parseResponse(searchResponse, HotelElastic.class);
        //解析总记录数
        long total = searchResponse.getHits().getTotalHits().value;

        //解析分组信息
        //所有聚合结果集
        Aggregations aggregations = searchResponse.getAggregations();
        //解析城市的GroupVo
        List<GroupVo> cityGroupVos = parseGroupVo(aggregations, "cityTerms", GroupVo.GroupType.CITY);
        //解析星级的GroupVo
        List<GroupVo> starGroupVos = parseGroupVo(aggregations, "starTerms", GroupVo.GroupType.STAR);
        //解析品牌的GroupVo
        List<GroupVo> brandGroupVos = parseGroupVo(aggregations, "brandTerms", GroupVo.GroupType.BRAND);

        //将数据封装到pageVo中
        pageVo.setList(hotelElastics);
        pageVo.setTotal(total);
        pageVo.setCityGroup(cityGroupVos);
        pageVo.setStarGroup(starGroupVos);
        pageVo.setBrandGroup(brandGroupVos);

        return pageVo;
    }

    /**
     *
     * @param aggregations  所有聚合结果集
     * @param aggName 要查询的聚合结果名称
     * @param type 分组的类型（城市、星级、品牌）
     * @return
     */
    private List<GroupVo> parseGroupVo(Aggregations aggregations, String aggName, GroupVo.GroupType type) {
        //或者指定名称的结果集
        Terms cityTerms = aggregations.get(aggName);
        //获取当前分组结果集中的bucket数据
        List<? extends Terms.Bucket> buckets = cityTerms.getBuckets();
        List<GroupVo> cityGroupVos = buckets.stream().map(bucket -> {
            String name = bucket.getKeyAsString();
            Long count = bucket.getDocCount();
            GroupVo groupVo = new GroupVo(type, name, count.intValue());
            return groupVo;
        }).collect(Collectors.toList());

        return cityGroupVos;
    }
}
